/*
 * Questa classe rappresenta l'intera rete fisica
 */

using namespace std;

class LogicalNet
{
    private:
    vector< vector<LogicalLink> > matrice;

	public:
	LogicalNet();
	LogicalNet(int n);
	int getASOrigin(int i, int j);
	int getASDestination(int i, int j);
	bool addLink(int i, int j, int bandwidth, int latency, bool state);
	list<int> getASPath(int i, int j);
	list< pair<int,int> > getASPathPair(int i, int j);
	bool setASPath(int i, int j, list<int>);
	int getBandwidth(int i, int j);
	int getLatency(int i, int j);
	bool getState(int i, int j);
	bool setState(int i, int j, bool state);
	bool resize(int n);
	bool init();
	int getSize();
	bool reset();
	bool calculateASPaths(PhysicalNet *reteFisica);
	list<int> percorsoMinimo (int i, int j, matrix pred);
	void floydWarshall (matrix *dist, matrix *pred);
	bool updateNet(PhysicalNet *reteFisica);
	bool updateLink(int x, int y, PhysicalNet *reteFisica);
	list < pair<int,int> > getASPathsThroughAS(int number);
    list < pair<int,int> > getASPathsThroughLink(int i, int j);
};

LogicalNet::LogicalNet()
{;}

LogicalNet::LogicalNet(int n)
{
    matrice.resize(n);
    for (int i=0; i<n; i++) matrice[i].resize(n);

    for (int i=0; i<n; i++)
        for (int j=0; j<n; j++)
            matrice[i][j] = LogicalLink(i, j, 0, 0, false);
}

int LogicalNet::getASOrigin(int i, int j)
{
    return matrice[i][j].getASOrigin();
}

int LogicalNet::getASDestination(int i, int j)
{
    return matrice[i][j].getASDestination();
}


bool LogicalNet::addLink(int i, int j, int bandwidth, int latency, bool state)
{
    matrice[i][j] = LogicalLink(i, j, bandwidth, latency, state);
}

list<int> LogicalNet::getASPath(int i, int j)
{
    return matrice[i][j].getASPath();
}

list< pair<int,int> > LogicalNet::getASPathPair(int i, int j)
{
    return matrice[i][j].getASPathPair();
}

bool LogicalNet::setASPath(int i, int j, list<int> ASPath)
{
    matrice[i][j].setASPath(ASPath);
}

int LogicalNet::getBandwidth(int i, int j)
{
    return matrice[i][j].getBandwidth();
}

int LogicalNet::getLatency(int i, int j)
{
    return matrice[i][j].getLatency();
}

bool LogicalNet::getState(int i, int j)
{
    return matrice[i][j].getState();
}

bool LogicalNet::setState(int i, int j, bool state)
{
    matrice[i][j].setState(state);
}

bool LogicalNet::resize(int n)
{
    matrice.resize(n);
    for(int i = 0;i < n;++i) matrice[i].resize(n);
}

bool LogicalNet::init()
{
    int n = matrice.size();
    for (int i=0; i<n; i++)
        for (int j=0; j<n; j++)
            matrice[i][j] = LogicalLink(i, j, 0, 0, false);
}

int LogicalNet::getSize()
{
    return matrice.size();
}

bool LogicalNet::reset()
{
    int n = matrice.size();
    for (int i=0; i<n; i++)
        for (int j=0; j<n; j++)
            matrice[i][j] = LogicalLink(i, j, 0, 0, false);
}

bool LogicalNet::calculateASPaths(PhysicalNet *reteFisica)
{
	//prepariamo distanze e predECESSORI
	//con floyd warshall calcoliamo gli aspath

	int i=0 ,j=0, n=reteFisica->getSize();

	//distanza=1 se connessi (come numero hop), sarebbe meglio usare una metrica diversaa
	matrix dist( n, vector<int>( n,INT_MAX/2 ) );
	matrix pred( n, vector<int>( n,0 ) );

	for(i=1; i<n; i++)
		for(j=1; j<n; j++)
		{
			if( i==j )
			{
				dist[i][j]= 0;
				pred[i][j]= i;
			}

			else
			//if( (reteFisica->getState(i, j)) && (reteFisica->getPolicy(i,j))!="peering" ) //se adiacenti e non in peering
			if( (reteFisica->getState(i, j))) //se adiacenti
			{
			    //cout << "calculateASPath: "<<i<<" e "<<j<<" sono vicini\n";
			    matrice[i][j].setState(true);
				dist[i][j]= 1;
				pred[i][j]= i;
			}
		}

	floydWarshall( &dist ,&pred);

	for( i=1; i<n ;i++ )
		for( j=1; j<n ;j++ )
		{
		    //cout << "calculateASPath: lancio percorsoMinimo tra "<<i<<" e "<<j<<endl;
		    matrice[i][j].setASPath(percorsoMinimo( i ,j ,pred));
		}

    for( i=1; i<n ;i++ )
		for( j=1; j<n ;j++ )
		{
            if( (reteFisica->getState(i, j)) && (reteFisica->getPolicy(i,j))=="peering" ) //se adiacenti e non in peering
            {
                list<int> path;
                path.push_back(i);
                path.push_back(j);
                matrice[i][j].setState(true);
                matrice[i][j].setASPath(path);
            }
        }
}

list<int> LogicalNet::percorsoMinimo (int i, int j, matrix pred)
{
	list<int> r;

	if (!matrice[i][j].getState())
        return r;

	int t=j;
	if( i == j )
		return r;
    //cout << "percorsominimo "<<i<<" "<<j<<": " << "inserisco " << j << " ";
    r.push_back( j );

	while( (t= pred[i][t] ) != 0 && t!= i  )
	{
		//cout << t << " ";
		r.push_back( t );
	}

	if( t==i && pred[i][j] != j )
	{
	    //cout << t << " \n";
	    r.push_back( t );
	}

	r.reverse();
	return r;
}

void LogicalNet::floydWarshall (matrix *dist, matrix *pred)
{
	//*dist per utilizzare le modifiche al di fuori di questo scope
	int h,i,j ,t ,n=(*dist).size();

	for(h=1; h<n; h++)
	{
		for(i=1; i<n; i++)
		{
			for(j=1; j<n; j++)
			{
				t= (*dist)[i][h] + (*dist)[h][j] ;
				if( t < (*dist)[i][j] )
				{
				    //cout << "floydWarshall: distanza "<<i<<" "<<j<<": "<<t<<endl;
					(*dist)[i][j] = t ;
					(*pred)[i][j]= (*pred)[h][j] ;
					matrice[i][j].setState(true);
				}
			}
		}
	}
}

bool LogicalNet::updateLink(int x, int y, PhysicalNet *reteFisica)
{
	int i,j,c,d,b;
	list<int>::iterator it;

	if( x == y )
		return false; //se stiamo in un gia in quell as non c'e nulla da calcolare

	list<int> ASPath = matrice[x][y].getASPath();
	it = ASPath.begin();
	i= *it;
	++it;
	c=0;
	for(;it != ASPath.end() ;i=*it ,++it )
	{
		j=*it;
		if( c++==0 )
		{
			d = reteFisica->getLatency(i, j);
			b = reteFisica->getBandwidth(i, j);
		}
		else
		{
			d = max(reteFisica->getLatency(i, j), d);
			b = min(reteFisica->getBandwidth(i, j) ,b );
		}
	}

	matrice[x][y].setLatency(d);
	matrice[x][y].setBandwidth(b);
}

bool LogicalNet::updateNet(PhysicalNet *reteFisica)
{
    int n = reteFisica->getSize();

    for (int i=1; i<n; i++)
		for (int j=1; j<n; j++)
		{
		    //if (matrice[i][j].getState())   // dele aggiornare tutti i link a prescindere
                updateLink(i, j, reteFisica);
		}
}

list < pair<int,int> > LogicalNet::getASPathsThroughAS(int number)
{
    list< pair<int, int> > lista;

    int n = matrice.size();
    for (int x=0; x<n; x++)
        for (int y=0; y<n; y++)
        {
            if (matrice[x][y].getState())
                if (matrice[x][y].isASInASPath(number))
                {
                    lista.push_back(make_pair (x,y));
                    //cout <<"l'arco "<<i<<" "<<j<<" è nell'aspath di "<<x<<" "<<y<<endl;
                }
        }

    return lista;
}

list < pair<int,int> > LogicalNet::getASPathsThroughLink(int i, int j)
{
    list< pair<int, int> > lista;

    int n = matrice.size();
    for (int x=0; x<n; x++)
        for (int y=0; y<n; y++)
        {
            if (matrice[x][y].getState())
                if (matrice[x][y].isLinkInASPath(i, j))
                {
                    lista.push_back(make_pair (x,y));
                    //cout <<"l'arco "<<i<<" "<<j<<" è nell'aspath di "<<x<<" "<<y<<endl;
                }
        }

    return lista;
}

